package model.mapping;

import java.util.LinkedList;
import java.util.List;

import model.item.Item;
import model.item.Obstacle;
import model.map.GameMap;
import model.map.GameTile;

public class ItemMapping
{
	private GameMap map;
	private PlayableMapping playableMapping;
	
	public void setPlayableMapping(PlayableMapping playableMapping)
	{
		this.playableMapping = playableMapping;
	}
	
	private class ItemTilePair
	{
		private Item item;
		private GameTile tile;
		
		public ItemTilePair(Item item, GameTile tile)
		{
			this.item = item;
			this.tile = tile;
		}
		
		public Item item()
		{
			return item;
		}
		
		public GameTile tile()
		{
			return tile;
		}

		public void trigger()
		{
			item.trigger();
		}
	}
	
	private class ObstacleTilePair
	{
		private Obstacle obs;
		private GameTile tile;
		
		public ObstacleTilePair(Obstacle obs, GameTile tile)
		{
			this.obs = obs;
			this.tile = tile;
		}
		
		public Obstacle obstacle()
		{
			return obs;
		}
		
		public GameTile tile()
		{
			return tile;
		}
	}
	
	private List<ItemTilePair> itemsOnMap;
	private List<ObstacleTilePair> obstaclesOnMap;
	
	private static ItemMapping itemMapping = new ItemMapping();
	
	private ItemMapping()
	{
		itemsOnMap = new LinkedList<ItemTilePair>();
		obstaclesOnMap = new LinkedList<ObstacleTilePair>();
	}
	
	public void setMap(GameMap map)
	{
		this.map = map;
	}
	
	// invariant: tile doesn't have an item or obstacle on it
	public void addItem(GameTile tile, Item item)
	{
		itemsOnMap.add(new ItemTilePair(item, tile));
	}
	
	// invariant: tile doesn't have an item or obstacle on it
	public void addObstacle(GameTile tile, Obstacle obs)
	{
		obstaclesOnMap.add(new ObstacleTilePair(obs, tile));
	}
	
	public void removeItem(Item item)
	{
		for (ItemTilePair itp : itemsOnMap)
		{
			if (itp.item() == item)
			{
				removeItemTilePair(itp);
				return;
			}
		}
	}
	
	public void removeItemFromTile(GameTile tile)
	{
		for (ItemTilePair itp : itemsOnMap)
		{
			if (itp.tile() == tile)
			{
				removeItemTilePair(itp);
				return;
			}
		}
	}
	
	private void removeItemTilePair(ItemTilePair itp)
	{
		itemsOnMap.remove(itp);
	}
	
	public void removeObstacle(Obstacle obstacle)
	{
		for (ObstacleTilePair otp : obstaclesOnMap)
		{
			if (otp.obstacle() == obstacle)
			{
				removeObstacleTilePair(otp);
				return;
			}
		}
	}
	
	public void removeObstacleFromTile(GameTile tile )
	{
		for (ObstacleTilePair otp : obstaclesOnMap)
		{
			if (otp.tile() == tile)
			{
				removeObstacleTilePair(otp);
				return;
			}
		}
	}
	
	private void removeObstacleTilePair(ObstacleTilePair otp)
	{
		itemsOnMap.remove(otp);
	}
	
	public GameTile getLocationOfItem(Item item)
	{
		for (ItemTilePair itp : itemsOnMap)
		{
			if (itp.item() == item)
				return itp.tile();
		}
		return null;
	}
	
	public GameTile getLocationOfObstacle(Obstacle obstacle)
	{
		for (ObstacleTilePair otp : obstaclesOnMap)
		{
			if (otp.obstacle() == obstacle)
				return otp.tile();
		}
		return null;
	}
	
	public Item getItemOn(GameTile tile)
	{
		for (ItemTilePair itp : itemsOnMap)
		{
			if (itp.tile() == tile)
				return itp.item();
		}
		
		return null;
	}
	
	public Obstacle getObstacleOn(GameTile tile)
	{	
		for (ObstacleTilePair otp : obstaclesOnMap)
		{
			if (otp.tile() == tile)
				return otp.obstacle();
		}
		
		return null;
	}
	
	public String getItemTypeOn(GameTile tile)
	{
		if (!isOccupied(tile))
		{
			return "NULL";
		}
		
		if (hasItem(tile))
		{
			return getItemOn(tile).getType();
		}
		else
			return getObstacleOn(tile).getType();
	}
	
	public boolean isBlocked(GameTile tile)
	{
		for (ObstacleTilePair otp : obstaclesOnMap)
		{
			if (otp.tile() == tile)
				return true;
		}
		return false;
	}
	
	public boolean hasItem(GameTile tile)
	{
		for (ItemTilePair itp : itemsOnMap)
		{
			if (itp.tile() == tile)
				return true;
		}
		return false;
	}
	
	public boolean isOccupied(GameTile tile)
	{
		return isBlocked(tile) || hasItem(tile);
	}
	
	public void triggerItemOn(GameTile tile)
	{
		if (!hasItem(tile))
			return;
		for (ItemTilePair itp : itemsOnMap)
		{
			if (itp.tile() == tile)
			{
				itp.trigger();
				return;
			}
		}
	}
	
	public void affectPlayablesOn(GameTile tile, Item item)
	{
		System.out.println(playableMapping == null);
		System.out.println("Item: " + (item == null));
		System.out.println("Tile: " + (tile == null));
		
		playableMapping.affectWithItem(tile, item);
	}
	
	public static ItemMapping getItemMapping()
	{
		return itemMapping;
	}

	public List<GameTile> getTilesAround(Item item) 
	{	
		return map.getTilesAround(getLocationOfItem(item), item.getRadius());
	}
	
}
